#define _GNU_SOURCE

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#define USAGE_ERROR "USAGE: utf8-bmp <file>\n"

/* Our UTF-8 Byte masks */
#define ASCII_MSK 0x80
#define TWO_MSK 0xE0
#define THREE_MSK 0xF0
#define EXT_MSK 0xF8

/* Our expected byte values */
#define ASCII_VAL 0x0
#define TWO_VAL 0xC0
#define THREE_VAL 0xE0
#define EXT_VAL 0xF0

enum {  ASCII_RANGE = 1,
        TWO_RANGE,
        THREE_RANGE,
        EXT_PLANE,
        ERROR };

short checkbyte(unsigned char byte);

int main (int argc, char **argv) {

    FILE *fd = NULL;
    int line = 1;
    size_t size = 0, count = 0;
    char *buffer = NULL;

    if (argc < 2) {
        fprintf(stderr, USAGE_ERROR);
        return -1;
    }

    if ((fd = fopen(argv[1], "r")) == NULL) {
        fprintf(stderr, "Could not open file, %s\n", argv[1]);
        return -1;
    }

    while (getline(&buffer, &size, fd) > 0) {
        for (count = 0; count < size && buffer[count] != '\n'; count++) {
            switch (checkbyte((unsigned char)buffer[count])) {
                case ASCII_RANGE:
                    count += 0;
                    break;
                case TWO_RANGE:
                    count += 1;
                    break;
                case THREE_RANGE:
                    count += 2;
                    break;
                case EXT_PLANE:
                    fprintf(stderr, "Found invalid character on line %d: %c\n", line, (unsigned char)buffer[count]);
                    count += size;
                    break;
                default:
                    fprintf(stderr, "Don't know what is going on with line %d: %X\n", line, buffer[count]);
/*                    fprintf(stderr, "count = %d \n %s\n", count, buffer); */
            }
        }
        line++;
   }
   free(buffer);
   fclose(fd);
   return 0;
}

short checkbyte(unsigned char byte) {
    if ((byte & ASCII_MSK) == ASCII_VAL) {
        return ASCII_RANGE;
    } else if ((byte & TWO_MSK) == TWO_VAL) {
        return TWO_RANGE;
    } else if ((byte & THREE_MSK) == THREE_VAL) {
        return THREE_RANGE;
    } else if ((byte & EXT_MSK) == EXT_VAL) {
        return EXT_PLANE;
    }
    return ERROR;
}

